#include "leam-lom-stream.hh"
#include "lom-context.hh"
#include "leam-stream.hh"
#include "tests.hh"

using namespace leam;
using namespace lom;
using namespace std;
using namespace boost;
using namespace leam_lom;
using namespace licada;



//Not sure basic describes this, but here we go...
class BasicLeamLomStreamTest : public TestBase
{
protected:
    LomContext leftContext;
    LomContext rightContext;
    LeamStream leftLeamStream;
    LeamStream rightLeamStream;
    LeamLomStream leftStream;
    LeamLomStream rightStream;
    
    TDataVector leftRight;
    TDataVector rightLeft;
    
    LeamVectorInStream lIn;
    LeamVectorInStream rIn;
    LeamVectorOutStream lOut;
    LeamVectorOutStream rOut;

public:
    virtual TNameType getName() { return "BasicLeamLomStreamTest"; }

    virtual void setup() {
	leftContext.setup();
	rightContext.setup();
	leftStream.setup( &leftContext, &leftLeamStream );
	rightStream.setup( &rightContext, &rightLeamStream );
	
	lIn.setup( &rightLeft );
	rIn.setup( &leftRight );
	lOut.setup( &leftRight );
	rOut.setup( &rightLeft );
	leftLeamStream.setup( &lIn, &lOut, &leftStream, false );
	rightLeamStream.setup( &rIn, &rOut, &rightStream, false );
    }
    virtual void testPrecondition() {
	TEST_EQ(0, leftContext.getClassManager()->getClassMap().size() );
	TEST_EQ(0, rightContext.getClassManager()->getClassMap().size() );
	TEST_EQ(0, leftContext.getInstanceManager()->getHandleToRoots().size() );
	TEST_EQ(0, rightContext.getInstanceManager()->getHandleToRoots().size() );
    }
    virtual void setupContext( const TNameStr& classPrefix
			       , ILomContext& context ) {
	LomTransactionGuard guard( context.getTransactionSystem()
				   , NULL );
	
	ILomInstanceManager& instanceMgr = *context.getInstanceManager();
	LomClassManager& classMgr = *context.getClassManager();
	
	TNameStr clsName("test_class_");
	clsName += classPrefix;
	
	LomClass cls(clsName.c_str(), context.getValueFactory());
	cls.addProperty( LomProperty("prop0", EDataTypeHandle) );
	cls.addProperty( LomProperty("prop1", EDataTypeInt64) );
	cls.addProperty( LomProperty("prop2", EDataTypeDouble), LomDouble(20));
	classMgr.addClass(cls);
	ILomInstanceFactory* instFac = context.getInstanceFactory();
	THandle handle = 
	    instFac->createInstance( 
		context.getClassManager()->getClass( clsName.c_str() ) );
	TNameStr rootName = "testObj_";
	rootName += classPrefix;
	context.setRootObject( rootName.c_str(), LomHandle( handle, NULL ) );
	LomInstance* test = context.getInstanceManager()->getInstance( handle );
	test->set( "prop1", LomInt64(42) );
	THandle hdl2 = instanceMgr.reserveHandle();
	LomHandle val( &instanceMgr );
	val.set( hdl2 );
	test->setIdx( 0, val );
	LomInstance* test2 = instanceMgr.getInstance( hdl2, false );
	TEST_EQ( NULL, test2 );
	const LomClass* lomCls = context.getClassManager()->getClass( 
	    clsName.c_str() );
	context.getInstanceFactory()->createInstance( lomCls, hdl2 );
    }
    virtual void doOperation() {
	setupContext( "left", leftContext );
	rightLeamStream.readAll();
	leftLeamStream.readAll();
    }
    //Ensure both contexts have the same information about things.
    virtual void testPostcondition() {
	checkContext( leftContext );
	checkContext( rightContext );
    }
private:
    void checkContext( ILomContext& context ) {
	TEST_TRUE( context.getRootObject( "testObj_left" ) );
	TEST_TRUE( context.getClassManager()->getClass( "test_class_left" ) );
	
	THandle hdl1 = context.getRootObject( "testObj_left" )->getHandle();
	LomInstance* inst1 = 
	    context.getInstanceManager()->getInstance( 
		context.getRootObject( "testObj_left" )->getHandle() );
	//Next section takes advantage of the fact that 
	//handles increment.
	uint32 refCount = inst1->getRefCount();
	TEST_EQ( 1, inst1->getRefCount() );
	TEST_EQ( 42, inst1->get( "prop1" ).getInt64() );
	//Ensure indexes match up
	THandle hdl2 = inst1->getIdx( 0 ).getHandle();
	
	LomInstance* inst2 = 
	    context.getInstanceManager()->getInstance( 
		inst1->getHandle() );
	
	TEST_NEQ( hdl1, hdl2 );
	TEST_EQ( 1, inst2->getRefCount() );
    }
};

/**
   Test that the context that did not generate an instance is
   capable of changing properties.  This ensures that the handle
   translation mechanism works both ways.
*/
class BasicLeamLomStreamTest2 : public BasicLeamLomStreamTest
{
public:
    virtual TNameType getName() { return "BasicLeamLomStreamTest2"; }
    
    virtual void setup() {
	BasicLeamLomStreamTest::setup();
	BasicLeamLomStreamTest::doOperation();
    }
    
    virtual void testPrecondition() {
	BasicLeamLomStreamTest::testPostcondition();
    }
    
    virtual void doOperation() {
	ILomContext& context = rightContext;
	setupContext( context );
	//Pump the 'network' connections
	rightLeamStream.readAll();
	leftLeamStream.readAll();
    }
    
    virtual void testPostcondition() {
	checkContext(leftContext);
	checkContext(rightContext);
    }
    
private:
    
    void setupContext( ILomContext& context ) {
	LomTransactionGuard guard( context.getTransactionSystem()
				   , NULL );
	LomInstance* inst =
	    context.getInstanceManager()->getInstance( 
		rightContext.getRootObject( "testObj_left" )->getHandle());
	inst->set( "prop2", LomDouble(666));
    }
    
    void checkContext(ILomContext& context) {
	THandle hdl1 = context.getRootObject( "testObj_left" )->getHandle();
	LomInstance* inst1 = 
	    context.getInstanceManager()->getInstance( 
		context.getRootObject( "testObj_left" )->getHandle() );
	TEST_EQ( 666, inst1->get( "prop2" ).getDouble() );
    }
};


int main( int, char** )
{
    //runTest< BasicLeamLomStreamTest >();
    runTest< BasicLeamLomStreamTest2 >();
    return 0;
}
